package com.bravo.other.geoid;

import java.util.Arrays;
import java.util.List;

/**
 * Advanced Geoid Height Calculator Test Class
 * 
 * @author bravo
 */
public class AdvancedGeoidTest {
    
    public static void main(String[] args) {
        System.out.println("=== Advanced Geoid Height Calculator Test ===\n");
        
        // Test coordinate point
        double latitude = 39.9042;  // Beijing Tiananmen
        double longitude = 116.4074;
        
        System.out.printf("Test coordinates: Latitude %.4f°, Longitude %.4f°\n", latitude, longitude);
        System.out.println();
        
        // 1. Test EGM2008 calculation with different degrees
        testDifferentDegrees(latitude, longitude);
        
        // 2. Test gradient calculation
        testGradientCalculation(latitude, longitude);
        
        // 3. Test curvature calculation
        testCurvatureCalculation(latitude, longitude);
        
        // 4. Test batch calculation
        testBatchCalculation();
        
        // 5. Performance test
        testPerformance();
        
        // 6. Accuracy comparison
        compareAccuracy();
    }
    
    /**
     * Test EGM2008 calculation with different degrees
     */
    private static void testDifferentDegrees(double latitude, double longitude) {
        System.out.println("=== EGM2008 Calculation Comparison with Different Degrees ===");
        
        int[] degrees = {5, 10, 15, 20, 25, 30};
        
        System.out.printf("%-8s %-15s %-15s %-15s\n", "Degree", "GeoidHeight(m)", "Time(ms)", "AccuracyImprovement");
        System.out.println("------------------------------------------------------------");
        
        double previousResult = 0.0;
        for (int degree : degrees) {
            long startTime = System.currentTimeMillis();
            double result = AdvancedGeoidCalculator.calculateEGM2008Complete(latitude, longitude, degree);
            long endTime = System.currentTimeMillis();
            
            double accuracyImprovement = 0.0;
            if (degree > 5) {
                accuracyImprovement = Math.abs(result - previousResult);
            }
            
            System.out.printf("%-8d %-15.6f %-15d %-15.6f\n", 
                degree, result, (endTime - startTime), accuracyImprovement);
            
            previousResult = result;
        }
        System.out.println();
    }
    
    /**
     * Test gradient calculation
     */
    private static void testGradientCalculation(double latitude, double longitude) {
        System.out.println("=== Geoid Height Gradient Calculation ===");
        
        double[] deltas = {0.001, 0.01, 0.1}; // Different small change amounts
        
        for (double delta : deltas) {
            double[] gradient = AdvancedGeoidCalculator.calculateGeoidGradient(latitude, longitude, delta);
            
            System.out.printf("Small change amount %.3f°:\n", delta);
            System.out.printf("  Latitude gradient (dN/dlat): %.6f m/°\n", gradient[0]);
            System.out.printf("  Longitude gradient (dN/dlon): %.6f m/°\n", gradient[1]);
            System.out.printf("  Gradient magnitude: %.6f m/°\n", Math.sqrt(gradient[0]*gradient[0] + gradient[1]*gradient[1]));
            System.out.println();
        }
    }
    
    /**
     * Test curvature calculation
     */
    private static void testCurvatureCalculation(double latitude, double longitude) {
        System.out.println("=== Geoid Height Curvature Calculation ===");
        
        double delta = 0.001;
        double[][] curvature = AdvancedGeoidCalculator.calculateGeoidCurvature(latitude, longitude, delta);
        
        System.out.println("Curvature matrix:");
        System.out.printf("  d²N/dlat²     = %.6f m/°²\n", curvature[0][0]);
        System.out.printf("  d²N/dlatdlon  = %.6f m/°²\n", curvature[0][1]);
        System.out.printf("  d²N/dlon²     = %.6f m/°²\n", curvature[1][1]);
        
        // Calculate principal curvatures
        double trace = curvature[0][0] + curvature[1][1];
        double determinant = curvature[0][0] * curvature[1][1] - curvature[0][1] * curvature[0][1];
        
        double discriminant = trace * trace - 4 * determinant;
        if (discriminant >= 0) {
            double k1 = (trace + Math.sqrt(discriminant)) / 2;
            double k2 = (trace - Math.sqrt(discriminant)) / 2;
            
            System.out.printf("Principal curvature k1 = %.6f m/°²\n", k1);
            System.out.printf("Principal curvature k2 = %.6f m/°²\n", k2);
            System.out.printf("Mean curvature = %.6f m/°²\n", (k1 + k2) / 2);
            System.out.printf("Gaussian curvature = %.6f m²/°⁴\n", k1 * k2);
        }
        System.out.println();
    }
    
    /**
     * Test batch calculation
     */
    private static void testBatchCalculation() {
        System.out.println("=== Batch Calculation Test ===");
        
        // Create test coordinate points
        List<AdvancedGeoidCalculator.Coordinate> coordinates = Arrays.asList(
            new AdvancedGeoidCalculator.Coordinate(39.9042, 116.4074), // Beijing
            new AdvancedGeoidCalculator.Coordinate(31.2304, 121.4737), // Shanghai
            new AdvancedGeoidCalculator.Coordinate(23.1291, 113.2644), // Guangzhou
            new AdvancedGeoidCalculator.Coordinate(30.2741, 120.1551), // Hangzhou
            new AdvancedGeoidCalculator.Coordinate(22.3193, 114.1694), // Shenzhen
            new AdvancedGeoidCalculator.Coordinate(29.8683, 121.5440), // Ningbo
            new AdvancedGeoidCalculator.Coordinate(32.0603, 118.7969), // Nanjing
            new AdvancedGeoidCalculator.Coordinate(36.6512, 117.1201)  // Jinan
        );
        
        long startTime = System.currentTimeMillis();
        List<AdvancedGeoidCalculator.GeoidResult> results = 
            AdvancedGeoidCalculator.calculateBatch(coordinates, 20);
        long endTime = System.currentTimeMillis();
        
        System.out.printf("Batch calculation of %d coordinate points, time: %d ms\n", coordinates.size(), (endTime - startTime));
        System.out.println();
        
        System.out.println("Calculation results:");
        for (AdvancedGeoidCalculator.GeoidResult result : results) {
            System.out.println(result.toString());
        }
        System.out.println();
    }
    
    /**
     * Performance test
     */
    private static void testPerformance() {
        System.out.println("=== Performance Test ===");
        
        double latitude = 39.9042;
        double longitude = 116.4074;
        int iterations = 1000;
        
        // Test performance with different degrees
        int[] degrees = {5, 10, 15, 20, 25, 30};
        
        System.out.printf("%-8s %-15s %-15s %-15s\n", "Degree", "SingleTime(μs)", "1000Times(ms)", "AvgTime(μs)");
        System.out.println("------------------------------------------------------------");
        
        for (int degree : degrees) {
            // Warm up
            for (int i = 0; i < 100; i++) {
                AdvancedGeoidCalculator.calculateEGM2008Complete(latitude, longitude, degree);
            }
            
            // Performance test
            long startTime = System.nanoTime();
            for (int i = 0; i < iterations; i++) {
                AdvancedGeoidCalculator.calculateEGM2008Complete(latitude, longitude, degree);
            }
            long endTime = System.nanoTime();
            
            long totalTime = endTime - startTime;
            double avgTime = totalTime / (double) iterations;
            
            System.out.printf("%-8d %-15.2f %-15.2f %-15.2f\n", 
                degree, avgTime/1000, totalTime/1000000, avgTime/1000);
        }
        System.out.println();
    }
    
    /**
     * Accuracy comparison
     */
    private static void compareAccuracy() {
        System.out.println("=== Accuracy Comparison Analysis ===");
        
        double latitude = 39.9042;
        double longitude = 116.4074;
        
        // Calculate using different methods
        double egm2008_5 = AdvancedGeoidCalculator.calculateEGM2008Complete(latitude, longitude, 5);
        double egm2008_10 = AdvancedGeoidCalculator.calculateEGM2008Complete(latitude, longitude, 10);
        double egm2008_20 = AdvancedGeoidCalculator.calculateEGM2008Complete(latitude, longitude, 20);
        double egm2008_30 = AdvancedGeoidCalculator.calculateEGM2008Complete(latitude, longitude, 30);
        
        System.out.println("EGM2008 calculation results with different degrees:");
        System.out.printf("  5 degrees:  %.6f m\n", egm2008_5);
        System.out.printf("  10 degrees: %.6f m\n", egm2008_10);
        System.out.printf("  20 degrees: %.6f m\n", egm2008_20);
        System.out.printf("  30 degrees: %.6f m\n", egm2008_30);
        System.out.println();
        
        System.out.println("Accuracy analysis:");
        System.out.printf("  Difference from 5 to 10 degrees: %.6f m\n", Math.abs(egm2008_10 - egm2008_5));
        System.out.printf("  Difference from 10 to 20 degrees: %.6f m\n", Math.abs(egm2008_20 - egm2008_10));
        System.out.printf("  Difference from 20 to 30 degrees: %.6f m\n", Math.abs(egm2008_30 - egm2008_20));
        System.out.println();
        
        System.out.println("Convergence analysis:");
        double convergence5_10 = Math.abs(egm2008_10 - egm2008_5);
        double convergence10_20 = Math.abs(egm2008_20 - egm2008_10);
        double convergence20_30 = Math.abs(egm2008_30 - egm2008_20);
        
        if (convergence10_20 < convergence5_10) {
            System.out.println("  Good convergence from 10 to 20 degrees");
        } else {
            System.out.println("  Moderate convergence from 10 to 20 degrees");
        }
        
        if (convergence20_30 < convergence10_20) {
            System.out.println("  Good convergence from 20 to 30 degrees");
        } else {
            System.out.println("  Moderate convergence from 20 to 30 degrees");
        }
        
        System.out.println();
        System.out.println("Recommendations:");
        System.out.println("  - For general applications, use 10-20 degrees");
        System.out.println("  - For high-precision applications, use 20-30 degrees");
        System.out.println("  - Beyond 30 degrees, accuracy improvement is limited but computation time increases significantly");
    }
}
